Removes ignored errors from 4 phpstan baseline files and fixes their errors - #13069
Removes ignored errors from 4 phpstan baseline files and fixes their errors#13069dpanta94 wants to merge 7 commits into
Conversation
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
c126a52 to
28772c5
Compare
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
4459ea8 to
986fda1
Compare
70396ef to
8775bb0
Compare
More specifically removes baselines deadCode.unreachable.neon, if.alwaysTrue.neon, while.alwaysFalse.neon and while.alwaysTrue.neon
8775bb0 to
8d02372
Compare
| <?php | ||
| while ( $ephemera->have_posts() ) : | ||
| $ephemera->the_post(); | ||
| $tmp_more = $GLOBALS['more']; |
There was a problem hiding this comment.
This fix addresses a 12-year-old issue introduced in 5ddc101 to fix Core-26961.
Give the test post content with a `<!--more-->` tag and count the resulting `more-link` anchors, so the test covers what the widget actually renders and not just the globals it leaves behind. Switch the manual output buffering to `get_echo()`, and correct the docblock: the `$more` global is zeroed by `WP_Query::setup_postdata()` on every `the_post()` call in the widget's secondary loop, not by the widget itself. A single post is enough. The widget saves `$more` before the loop rather than inside it, so a second post adds nothing to the restore assertion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reading `$GLOBALS['more']` before the widget's secondary loop is only safe once some loop has run, since `WP_Query::setup_postdata()` is the only thing that ever defines the global. The widget can render before that happens: on a 404, an empty search, or an empty archive the main loop never calls `the_post()`, and the sidebar then emits a warning even though the widget's own query has posts. The same applies to renders outside the template, such as the widget endpoints and the Customizer preview. Default to null so the save still happens and the later restore stays a plain assignment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The suite runs with `backupGlobals` disabled and nothing else restores `$more` or `$content_width`, so these tests left their values in place for every later test in the process. `tests/phpunit/tests/media.php` reads `$content_width` and only falls back to 640 when it is empty, so the leak can produce order-dependent failures well away from here. Capture both globals in `set_up()` and put them back in `tear_down()`, unsetting those that were not set to begin with. The capture happens before the skip for a missing theme, since that still runs `tear_down()`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…or PHPStan. Both `WP_Query::have_posts()` and `WP_Query::have_comments()` advance the loop as a side effect, so two identical calls do not return the same value. PHPStan treated them as pure, concluded that a `while ( have_posts() )` condition could never change, and reported the loops as always-true or always-false with the code past them unreachable. Marking those methods and their procedural wrappers in `wp-includes/query.php` as `@phpstan-impure` empties the `while.alwaysTrue` and `while.alwaysFalse` baselines, which are deleted along with their entries in `phpstan.neon.dist`, and trims `deadCode.unreachable` and `if.alwaysTrue`. Two of the removed `while.alwaysTrue` entries were in `Twenty_Fourteen_Ephemera_Widget::widget()`, whose loop carries a real bug: `$tmp_more` was assigned inside the loop whose effects it exists to undo, so on every iteration after the first it captured the value the widget itself had just written, and the restore afterwards put back the secondary query's `0` rather than the caller's value. The assignment moves above the loop, while also guarding against the variable possibly being undefined. Developed in #13069. Follow-up to r27124, r30085, r63023. Props dpantazis, westonruter. See #65817. git-svn-id: https://develop.svn.wordpress.org/trunk@63357 602fd350-edb4-49c9-b593-d223f7449a82
…or PHPStan. Both `WP_Query::have_posts()` and `WP_Query::have_comments()` advance the loop as a side effect, so two identical calls do not return the same value. PHPStan treated them as pure, concluded that a `while ( have_posts() )` condition could never change, and reported the loops as always-true or always-false with the code past them unreachable. Marking those methods and their procedural wrappers in `wp-includes/query.php` as `@phpstan-impure` empties the `while.alwaysTrue` and `while.alwaysFalse` baselines, which are deleted along with their entries in `phpstan.neon.dist`, and trims `deadCode.unreachable` and `if.alwaysTrue`. Two of the removed `while.alwaysTrue` entries were in `Twenty_Fourteen_Ephemera_Widget::widget()`, whose loop carries a real bug: `$tmp_more` was assigned inside the loop whose effects it exists to undo, so on every iteration after the first it captured the value the widget itself had just written, and the restore afterwards put back the secondary query's `0` rather than the caller's value. The assignment moves above the loop, while also guarding against the variable possibly being undefined. Developed in WordPress/wordpress-develop#13069. Follow-up to r27124, r30085, r63023. Props dpantazis, westonruter. See #65817. Built from https://develop.svn.wordpress.org/trunk@63357 git-svn-id: http://core.svn.wordpress.org/trunk@62550 1a063a9b-81f0-0310-95a4-ce76da25c4cd
✅ Committed in r63357 (1806675).
Removes in total
104errorsRemoves errors from the below phpstan baselines and fixes the issues that they were covering:
How ?
Marks the
have_comments()andhave_posts()methods as impure since they can change their return type based on the state of a speficicWP_Queryinstance or the global'sWP_Queryinstance.In the file
src/wp-content/themes/twentyfourteen/inc/widgets.phpmoves the$tmp_moredefinition out of the while loop$ephemera->have_posts(). This was a bug, since it would guarantee thatGLOBALS['more']and the$tmp_morewould be overwritten to 0 if there were at least 2 loops.Loom showing the bug https://www.loom.com/share/c713c48d553b4e6abfdb3e2b9b5f2cf7
Follow-up fix to the theme change
Moving that definition out of the loop also changed when the global is read. Inside the loop it was always preceded by
$ephemera->the_post(), andWP_Query::setup_postdata()is the only thing in core that ever defines$more. Read before the loop, the global is not guaranteed to exist, and the widget can render before any loop has run — on a 404, an empty search, or an empty archive, where the main loop never callsthe_post()but the widget's own query still has posts. The same applies to renders outside the template, such as the widget REST endpoints and the Customizer preview. That produced:The read is now
$GLOBALS['more'] ?? null, so the save still happens and the later restore stays a plain assignment.Note that the
$GLOBALS['more'] = 0;assignment inside the loop has been redundant since r30085, which gavesetup_postdata()an explicitelse { $more = 0; }branch for secondary loops. It is left in place as harmless defensive code.Tests
tests/phpunit/tests/theme/twentyFourteenEphemeraWidget.phpcoversTwenty_Fourteen_Ephemera_Widget::widget():test_widget_restores_more_global()gives the post a<!--more-->tag and asserts the widget renders a teaser with a Read More link, then restores both$moreand$content_width. Removing either restore from the widget fails it.test_widget_when_more_global_is_undefined()renders the widget with$moreunset, covering the guard above. Removing the guard fails it.Both globals are captured in
set_up()and put back intear_down(), since the suite runs withbackupGlobalsdisabled and$content_widthin particular feeds calculations in other test files.Trac ticket: https://core.trac.wordpress.org/ticket/65817
Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Opus 5
Used for: Help with suggesting how to fix the phpstan output for the specific errors we are removing the baselines for. The actual result has been reviewed and are owned by me.
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.